#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/completion.h>

#if 0
在Linux内核中，struct completion是一个用于同步的数据结构
通常用于等待某个事件完成并唤醒等待它的线程
该结构定义在 <linux/completion.h> 头文件中。
要使用 struct completion，首先需要初始化它。
可以使用 init_completion(struct completion *x) 
函数来初始化一个 struct completion 对象。
然后，在某个线程中，可以通过调用 wait_for_completion(struct completion *x)
函数来等待事件的完成。如果事件尚未发生，调用线程将会被挂起，直到事件完成。
另外，当事件完成时，可以调用 complete(struct completion *x)
函数来唤醒所有等待该事件的线程。
下面是一个简单示例代码，演示了如何使用 struct completion：

#include <linux/completion.h>

struct completion comp;

// 初始化 completion
init_completion(&comp);

// 在某个线程中等待 completion 完成
wait_for_completion(&comp);

// 当事件完成时唤醒等待该事件的线程
complete(&comp);

#endif


struct spi_vir_driver_data {
    struct spi_bitbang bitbang; /* bitbang has to be first */
    struct spi_master *master;
    struct platform_device *pdev;
    struct completion xfer_done;
};

/* 在中断函数中唤醒阻塞线程事件 xxx_isr() { complete(&g_xfer_done)  } */

static int spi_vir_master_transfer(struct spi_device *spi, struct spi_transfer *transfer)
{   
    struct spi_vir_driver_data *svdd = spi_master_get_devdata(spi->master);
    int timeout;
    int ret = transfer->len;
    
    /* 重新初始化事件 */
    reinit_completion(&svdd->xfer_done);

    /* 硬件传输完成中断唤醒,所有等待该事件 */
    complete(&svdd->xfer_done);

    /* 等待事件唤醒超时时间  */
    timeout = wait_for_completion_timeout(&svdd->xfer_done, 100);
    if (!timeout) {
		dev_err(&spi->dev, "I/O Error in PIO\n");
		ret = -ETIMEDOUT;
	}

    return ret;
}

static void spi_vir_master_chipselect(struct spi_device *spi, int is_active)
{
    
}

static int spi_vir_master_probe(struct platform_device *pdev)
{
    struct spi_master *master = NULL;
    int ret = 0;
    struct spi_vir_driver_data *svdd = NULL;

    /* 分配/设置/注册spi_master */
    master = spi_alloc_master(&pdev->dev, sizeof(struct spi_vir_driver_data));
    if (master == NULL) {
        dev_err(&pdev->dev, "Unable to allocate SPI Master\n");
		return -ENOMEM;
    }

    /* 设置私有数据 */
    svdd = spi_master_get_devdata(master);
    platform_set_drvdata(pdev, svdd);
    svdd->master = master;
    svdd->pdev = pdev;

    /* 初始化completion */
    init_completion(&svdd->xfer_done);

    /* 设置bitbang  _master */
    svdd->bitbang.master = master;

    /* 设置txrx_bufs函数 */
    svdd->bitbang.txrx_bufs = spi_vir_master_transfer;

    /* 设置chipselect函数 */
    svdd->bitbang.chipselect = spi_vir_master_chipselect;

    /* 设置master设备树 */
    master->dev.of_node = pdev->dev.of_node;

    /* 绑定spi_bitbang */
    ret = spi_bitbang_start(&svdd->bitbang);
	if (ret) {
		dev_err(&pdev->dev, "bitbang start failed with %d\n", ret);
		spi_master_put(master);
        return ret;
	}

    return 0;
}

static int spi_vir_master_remove(struct platform_device *pdev)
{
    /* 反注册spi_master */
    struct spi_vir_driver_data *svdd = platform_get_drvdata(pdev);
    spi_bitbang_stop(&svdd->bitbang);
    spi_master_put(svdd->master);
    
    return 0;
}

static const struct of_device_id spi_vir_master[] = {
    { .compatible = "jz2440,virtual_spi_master", },
    {},
};

static struct platform_driver spi_vir_master_driver = {
    .probe = spi_vir_master_probe,
    .remove = spi_vir_master_remove,
    .driver = {
        .name = "jz-spi-vir-master",
        .of_match_table = of_match_ptr(spi_vir_master),
    }
};

static int spi_vir_master_init(void)
{
    int ret = platform_driver_register(&spi_vir_master_driver);
    if (ret < 0) {
        dev_err(NULL, "spi_vir_master_init failed");
        return -EBUSY;
    }

    return 0;
}

static void spi_vir_master_exit(void)
{
    platform_driver_unregister(&spi_vir_master_driver);
}

module_init(spi_vir_master_init);
module_exit(spi_vir_master_exit);

MODULE_AUTHOR("huang");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("spi master driver");





